import { WebSocket, WebSocketServer } from 'ws'
import { webCamPort as wsPort } from '../../common/constants.js'

const VideoStream = require('./node-rtsp-stream/videoStream.gai.js')
const Onvif = require('./node-onvif/node-onvif.js')
const zhenLv = 30 // 帧率
const STREAM_MAGIC_BYTES = 'jsmp' // Must be 4 bytes
const pathToFfmpeg = require('ffmpeg-static')

// 操作系统平台不同，需要重装ffmpeg-static
export const initWebwCamService = () => {
  let ffmpegMissing = false

  const wsServer = new WebSocketServer({ port: wsPort })

  const stopStream = (videoStream) => {
    if (videoStream) {
      videoStream.stop()
    }
  }
  // 开始视频流
  const startStream = (params, ws, playback) => {
    if (ffmpegMissing) return

    stopStream(ws.videoStream)

    let {
      username,
      password,
      ip,
      channel = 1, // 通道
      subtype = 1, // 码流
      startTime,
      endTime
    } = params

    let ch = channel + String(subtype).padStart(2, '0')
    let width = 1920
    let height = 1080

    let options = {
      name: 'name',
      streamUrl: `rtsp://${username}:${password}@${ip}:554/Streaming/Channels/${ch}`,
      ffmpegPath: pathToFfmpeg,
      ffmpegOptions: {
        // options ffmpeg flags
        '-an': '', // 不拉取音频流
        '-stats': '', // an option with no neccessary value uses a blank string
        '-r': zhenLv, // options with required values specify the value after the key
        '-s': `${width}*${height}`
      }
    }

    if (playback) {
      // 用的本地时间，不用转UTC
      options.streamUrl = `rtsp://${username}:${password}@${ip}:554/Streaming/tracks/${ch}?starttime=${startTime}&endtime=${endTime}`
    }

    const videoStream = new VideoStream(options)

    ws.videoStream = videoStream

    videoStream.on('startStream', () => {
      ws.send(
        JSON.stringify({
          code: 200,
          message: '取流成功'
        })
      )
    })

    videoStream.on('camdata', (data) => {
      ws.send(data)
    })

    videoStream.on('errMessage', (data) => {
      let dataStr = data.toString()
      if (dataStr.includes('Server returned 400 Bad Request')) {
        ws.send(
          JSON.stringify({
            code: 400,
            message: '无监控数据'
          })
        )
      }
    })
  }
  // 连接onvif设备
  const initOnvif = (params, ws) => {
    const { username, password, ip } = params
    const xaddr = `http://${ip}/onvif/device_service`
    const onvifDevice = new Onvif.OnvifDevice({ xaddr })
    onvifDevice.setAuth(username, password)
    onvifDevice.init((error, result) => {
      let res = { type: 'initOnvif', code: 200 }

      if (error) {
        res.code = 400
        res['message'] = error.toString()
      } else res['result'] = result

      ws.send(JSON.stringify(res))
    })

    ws.onvifDevice = onvifDevice
  }
  // 海报帧
  const fetchSnapshot = (ws) => {
    const device = ws.onvifDevice
    if (!device) return

    device.fetchSnapshot((error, result) => {
      var res = { type: 'fetchSnapshot', code: 200 }
      if (error) {
        res.code = 400
        res['message'] = error.toString()
      } else {
        var ct = result['headers']['content-type']
        var buffer = result['body']
        var b64 = buffer.toString('base64')
        var uri = 'data:' + ct + ';base64,' + b64
        res['result'] = uri

        // For Debug --------------------------------------------
        /*
        total_size += parseInt(result['headers']['content-length'], 10);
        var duration = Date.now() - start_time;
        var bps = total_size * 1000 / duration;
        var kbps = parseInt(bps / 1000);
        total_frame ++;
        var fps = Math.round(total_frame * 1000 / duration);
        console.log(kbps + ' kbps / ' + fps + ' fps');
        */
        // ------------------------------------------------------
      }
      ws.send(JSON.stringify(res))
    })
  }
  // ptz
  const ptzHome = (params, ws) => {
    const device = ws.onvifDevice
    if (!device) return

    if (!device.services.ptz) {
      var res = {
        code: '400',
        message: '设备不支持PTZ云台控制'
      }
      ws.send(JSON.stringify(res))
      return
    }

    var ptz = device.services.ptz
    var profile = device.getCurrentProfile()
    var params = {
      ProfileToken: profile['token'],
      Speed: 1
    }

    ptz.gotoHomePosition(params, (error, result) => {
      let res = { type: 'ptzHome', code: 200 }

      if (error) {
        res.code = 400
        res['message'] = error.toString()
      } else res['result'] = result

      ws.send(JSON.stringify(res))
    })
  }
  const ptzMove = (params, ws) => {
    const device = ws.onvifDevice
    if (!device) return

    device.ptzMove(params, (error) => {
      let res = { type: 'ptzMove', code: 200 }

      if (error) {
        res.code = 400
        res['message'] = error.toString()
      } else res['result'] = '操作成功'

      ws.send(JSON.stringify(res))
    })
  }
  const ptzStop = (ws) => {
    const device = ws.onvifDevice
    if (!device) return

    device.ptzStop((error) => {
      let res = { type: 'ptzStop' }

      if (error) {
        res.code = 400
        res['message'] = error.toString()
        ws.send(JSON.stringify(res))
      }
    })
  }
  // 广播
  const broadcast = () => {
    for (let client of wsServer.clients) {
      if (client.readyState === WebSocket.OPEN) {
      }
    }
  }
  // 消息处理
  /**
   *
   * @param {Uint8Array} dataBuffer
   */
  const onMessage = (dataBuffer, ws) => {
    let data = JSON.parse(dataBuffer) // JSON.parse 参数调用toString

    if (data.method == 'initOnvif') initOnvif(data.params, ws)
    if (data.method == 'fetchSnapshot') fetchSnapshot(ws)
    if (data.method == 'startStream') startStream(data.params, ws)
    if (data.method == 'playback') startStream(data.params, ws, true)
    if (data.method == 'stopStream') stopStream(ws.videoStream)
    if (data.method == 'ptzHome') ptzHome(data.params, ws)
    if (data.method == 'ptzMove') ptzMove(data.params, ws)
    if (data.method == 'ptzStop') ptzStop(ws)
  }
  // 心跳
  function heartbeat() {
    this.isAlive = true
  }
  // ws服务事件
  wsServer.on('connection', (ws, request) => {
    // 客户端会自动发送pong
    ws.on('pong', heartbeat)

    let streamHeader = Buffer.alloc(8)
    streamHeader.write(STREAM_MAGIC_BYTES)
    streamHeader.writeUInt16BE(this.width, 4)
    streamHeader.writeUInt16BE(this.height, 6)
    ws.send(streamHeader, {
      binary: true
    })

    ws.on('message', (data) => onMessage(data, ws))
    ws.on('close', () => stopStream(ws.videoStream))
  })

  const interval = setInterval(function ping() {
    wsServer.clients.forEach(function each(ws) {
      if (ws.isAlive === false) return ws.terminate()

      ws.isAlive = false
      ws.ping()
    })
  }, 3000)

  wsServer.on('close', () => {
    clearInterval(interval)
  })
}
